﻿using System;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using élénkPróbája.extentions;
using élénkPróbája.results;
using élénkPróbája.Test.dto;
using élénkPróbája.Test.extention;

namespace élénkPróbája.Test
{
    [TestClass]
    public class AbstractValidatorTester
    {
        TestValidator validator;

        public AbstractValidatorTester()
        {
            CultureScope.SetDefaultCulture();
            validator = new TestValidator();
        }

        [TestMethod]
        public void When_the_Validators_pass_then_the_validatorRunner_should_return_true()
        {
            validator.RuleFor(x => x.Name).NotNull();
            Assert.IsTrue(validator.Validate(new Company() {Name = "Jason"}).IsValid);
        }

        [TestMethod]
        public void When_the_validators_fail_then_validatorrunner_should_return_false()
        {
            validator.RuleFor(x => x.Name).NotNull();
            validator.Validate(new Company()).IsValid.ShouldBeFalse();
        }

        [TestMethod]
        public void When_the_validators_fail_then_the_errors_Should_be_accessible_via_the_errors_property()
        {
            validator.RuleFor(x => x.Name).NotNull();
            var result = validator.Validate(new Company());
            result.Errors.Count.ShouldEqual(1);
        }

        [TestMethod]
        public void Should_validate_public_Field()
        {
            validator.RuleFor(x => x.Name).NotNull();
            var result = validator.Validate(new Company());
            result.Errors.Count.ShouldEqual(1);
        }

        [TestMethod]
        public void WithMessage_should_override_error_message()
        {
            validator.RuleFor(x => x.Name).NotNull().WithMessage("Foo");
            var result = validator.Validate(new Company());
            result.Errors[0].ErrorMessage.ShouldEqual("Foo");
        }

        [TestMethod]
        public void WithErrorCode_should_override_error_code()
        {
            validator.RuleFor(x => x.Name).NotNull().WithErrorCode("ErrCode101");
            var result = validator.Validate(new Company());
            result.Errors[0].ErrorCode.ShouldEqual("ErrCode101");
        }

        [TestMethod]
        public void WithMessage_and_WithErrorCode_should_override_error_message_and_error_code()
        {
            validator.RuleFor(x => x.Name).NotNull().WithMessage("Foo").WithErrorCode("ErrCode101");
            var result = validator.Validate(new Company());
            result.Errors[0].ErrorMessage.ShouldEqual("Foo");
            result.Errors[0].ErrorCode.ShouldEqual("ErrCode101");
        }

        [TestMethod]
        public void WithName_should_override_field_name()
        {
            validator.RuleFor(x => x.Name).NotNull().WithName("First Name");
            var result = validator.Validate(new Company());
            result.Errors[0].ErrorMessage.ShouldEqual("'First Name' should not be empty.");
        }

        [TestMethod]
        public void WithPropertyName_should_override_property_name()
        {
            validator.RuleFor(x => x.Name).NotNull().OverridePropertyName("foo");
            var result = validator.Validate(new Company());
            result.Errors[0].PropertyName.ShouldEqual("foo");
        }
        [TestMethod]
        public void Should_not_main_state()
        {
            validator.RuleFor(x => x.Name).NotNull();
            validator.Validate(new Company());
            var result = validator.Validate(new Company());
            result.Errors.Count.ShouldEqual(1);
        }
        [TestMethod] 
        public void Should_throw_when_rule_is_null()
        {
            MyAssert.Throws<ArgumentNullException>(() => validator.RuleFor<string>(null));
        }
        [TestMethod]
        public void Should_throw_when_custom_rule_is_null()
        {
            MyAssert.Throws<ArgumentNullException>(() => validator.Custom((Func<Company, ValidationFailure>)null));
        }
        [TestMethod]
        public void Should_throw_when_customasync_rule_is_null()
        {
            MyAssert.Throws<ArgumentNullException>(() => validator.CustomAsync((Func<Company, Task<ValidationFailure>>)null));
        }
        [TestMethod]
        public void Should_validate_single_property()
        {
            validator.RuleFor(x => x.Name).NotNull();
            validator.RuleFor(x => x.Id).NotNull();
            validator.Validate(new Company());
            var result = validator.Validate(new Company(), x => x.Name);
            result.Errors.Count.ShouldEqual(1);
        }
        [TestMethod]
        public void Should_validate_single_Field()
        {
            validator.RuleFor(x => x.Name).NotNull(); 
            validator.Validate(new Company());
            var result = validator.Validate(new Company(), x => x.Name);
            result.Errors.Count.ShouldEqual(1);
        }
        [TestMethod]
        public void Should_throw_for_non_member_expression_when_validating_single_property()
        {
            MyAssert.Throws<ArgumentException>(() => validator.Validate(new Company(), x => "foo"));
        }
        [TestMethod]
        public void Should_be_valid_when_there_are_no_failures_for_single_property()
        {
            validator.RuleFor(x => x.Name).NotNull();
            validator.Validate(new Company());
            var result = validator.Validate(new Company {Name = "foo"}, x => x.Name);
            result.IsValid.ShouldBeTrue();
        }
        [TestMethod]
        public void Should_validate_single_property_where_property_as_string()
        {
            validator.RuleFor(x => x.Name).NotNull();
            validator.RuleFor(x => x.Id).NotNull(); 
            var result = validator.Validate(new Company() ,"Name");
            result.Errors.Count.ShouldEqual(1);
        }
        [TestMethod]
        public void Should_validate_single_property_where_invalid_property_as_string()
        {
            validator.RuleFor(x => x.Name).NotNull();
            validator.RuleFor(x => x.Id).NotNull();
            var result = validator.Validate(new Company(), "Name1");
            result.Errors.Count.ShouldEqual(0);
        }

        [TestMethod]
        public void CanValidateInstancesOfType_returns_true_when_comparing_against_same_type()
        {
            var validator = (IValidator)this.validator;
            validator.CanValidateInstancesOfType(typeof(Company)).ShouldBeTrue();
        }
        [TestMethod]
        public void CanValidateInstancesOfType_returns_true_when_comparing_against_subclass()
        {
            var validator = (IValidator)this.validator;
            validator.CanValidateInstancesOfType(typeof(DerivedCompany)).ShouldBeTrue();
        }
        [TestMethod]
        public void CanValidateInstancesOfType_returns_false_when_comparing_against_some_other_type()
        {
            var validator = (IValidator)this.validator;
            validator.CanValidateInstancesOfType(typeof(Address)).ShouldBeFalse();
        }
        [TestMethod]
        public void Uses_named_parameters_to_validate_ruleset()
        {
            validator.RuleSet("Names", () => {
                validator.RuleFor(x => x.Name).NotNull(); 
            });
            validator.RuleFor(x => x.Id).NotEqual(0);

            var result = validator.Validate(new Company(), ruleSet: "Names");
            result.Errors.Count.ShouldEqual(1);
        }
        [TestMethod]
        public void Validates_type_when_using_non_generic_validate_overload()
        {
            IValidator nonGenericValidator = validator;
            MyAssert.Throws<InvalidOperationException>(() =>
                nonGenericValidator.Validate("foo"));
        } 

        private class DerivedCompany : Company { }
    }
}
